﻿//"The contents of this file are subject to the Mozilla Public License
//Version 1.1 (the "License"); you may not use this file except in
//compliance with the License. You may obtain a copy of the License at
//http://www.mozilla.org/MPL/

//Software distributed under the License is distributed on an "AS IS"
//basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
//License for the specific language governing rights and limitations
//under the License.

//The Original Code is TSharp.

//The Initial Developer of the Original Code is Mar3ek (http://mar3ek.wordpress.com).
//Portions created by Mar3ek are Copyright (C) 2010
//Mar3ek (http://mar3ek.wordpress.com). All Rights Reserved.

//Contributor(s): ______________________________________.

//Alternatively, the contents of this file may be used under the terms
//of the _____ license (the  "[___] License"), in which case the
//provisions of [______] License are applicable instead of those
//above.  If you wish to allow use of your version of this file only
//under the terms of the [____] License and not to allow others to use
//your version of this file under the MPL, indicate your decision by
//deleting  the provisions above and replace  them with the notice and
//other provisions required by the [___] License.  If you do not delete
//the provisions above, a recipient may use your version of this file
//under either the MPL or the [___] License."

using System;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Web;

namespace TSharp
{
    /// <summary>
    /// Class containing methods for OAuth authentication
    /// </summary>
    public static class OAuth
    {
        /// <summary>
        /// Requests a new unathorized token from Twitter (using SSL)
        /// </summary>
        /// <param name="consumerKey">Application consumer key</param>
        /// <param name="consumerSecret">Application consumer secret</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <returns>Unauthorized token</returns>
        public static OAuthToken GetRequestToken(string consumerKey, string consumerSecret)
        {
            if (string.IsNullOrEmpty(consumerKey)) throw new ArgumentNullException("consumerKey");
            if (string.IsNullOrEmpty(consumerSecret)) throw new ArgumentNullException("consumerSecret");

            const string method = "POST";
            const string baseAddress = "https://api.twitter.com/oauth/request_token";
            const string parameters =
                "oauth_callback=oob&oauth_consumer_key={0}&oauth_nonce={1}&oauth_signature_method={2}&oauth_timestamp={3}&oauth_version=1.0";

            var timestamp = DateTime.Now.Subtract(new DateTime(1970, 1, 1, 0, 0, 0));
            timestamp = timestamp.Subtract(Globals.TwitterTimeDifference);
            var nonce = Globals.CreateNonce();
            const string oAuthSignatureMethod = "HMAC-SHA1";

            var totalSeconds = (int)timestamp.TotalSeconds;
            var sigBaseString = method + "&" + Globals.ProperUrlEscape(baseAddress) + "&" +
                                   Globals.ProperUrlEscape(string.Format(parameters, consumerKey, nonce, oAuthSignatureMethod,
                                                                 totalSeconds));

            string oAuthSignature;

            using (var sha = new HMACSHA1(Encoding.ASCII.GetBytes(consumerSecret + "&")))
            {
                oAuthSignature = Convert.ToBase64String(sha.ComputeHash(Encoding.ASCII.GetBytes(sigBaseString)));
            }

            var oAuthHeader =
                string.Format(
                    "OAuth oauth_callback=\"oob\", oauth_consumer_key=\"{0}\", oauth_nonce=\"{1}\", oauth_signature_method=\"{2}\", oauth_timestamp=\"{3}\", oauth_signature=\"{4}\", oauth_version=\"1.0\"",
                    consumerKey, nonce, oAuthSignatureMethod, totalSeconds, Globals.ProperUrlEscape(oAuthSignature));

            var request = (HttpWebRequest) WebRequest.Create(new Uri(baseAddress));

            request.Method = method;
            request.Headers.Add("Authorization", oAuthHeader);

            try
            {
                var response = request.GetResponse();

// ReSharper disable AssignNullToNotNullAttribute
                using (var sr = new StreamReader(response.GetResponseStream()))
                {
                    var result = sr.ReadToEnd();
                    var collection = HttpUtility.ParseQueryString(result);
                    return new OAuthToken(collection["oauth_token"], collection["oauth_token_secret"]);
                }
// ReSharper restore AssignNullToNotNullAttribute
            }
            catch (WebException)
            {
                return null;
            }
        }

        /// <summary>
        /// Retrieves an authenticated token used for making requests on user's behalf
        /// </summary>
        /// <param name="consumerKey">Application consumer key</param>
        /// <param name="consumerSecret">Application consumer secret</param>
        /// <param name="token">Unathorized token</param>
        /// <param name="pin">Authorization PIN</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <returns>Authorized token as OAuthToken, null on exception</returns>
        /// <exception cref="OAuthException"><c>OAuthException</c>.</exception>
        public static OAuthToken GetAccessToken(string consumerKey, string consumerSecret, OAuthToken token, string pin)
        {
            if (string.IsNullOrEmpty(consumerKey)) throw new ArgumentNullException("consumerKey");
            if (string.IsNullOrEmpty(consumerSecret)) throw new ArgumentNullException("consumerSecret");
            if (string.IsNullOrEmpty(pin)) throw new ArgumentNullException("pin");
            if (token == null) throw new ArgumentNullException("token");

            const string method = "POST";
            const string baseAddress = "https://api.twitter.com/oauth/access_token";
            const string parameters =
                "oauth_consumer_key={0}&oauth_nonce={1}&oauth_signature_method={2}&oauth_timestamp={3}&oauth_token={4}&oauth_verifier={5}&oauth_version=1.0";

            var timestamp = DateTime.Now.Subtract(new DateTime(1970, 1, 1, 0, 0, 0));
            timestamp = timestamp.Subtract(Globals.TwitterTimeDifference);
            var nonce = Globals.CreateNonce();
            const string oAuthSignatureMethod = "HMAC-SHA1";

            var totalSeconds = (int)timestamp.TotalSeconds;
            var sigBaseString = method + "&" + Globals.ProperUrlEscape(baseAddress) + "&" +
                                   Globals.ProperUrlEscape(string.Format(parameters, consumerKey, nonce, oAuthSignatureMethod,
                                                                 totalSeconds, token.Token, pin));

            string oAuthSignature;

            using (var sha = new HMACSHA1(Encoding.ASCII.GetBytes(consumerSecret + "&" + token.TokenSecret)))
            {
                oAuthSignature = Convert.ToBase64String(sha.ComputeHash(Encoding.ASCII.GetBytes(sigBaseString)));
            }

            var oAuthHeader =
                string.Format(
                    "OAuth oauth_consumer_key=\"{0}\", oauth_nonce=\"{1}\", oauth_signature_method=\"{2}\", oauth_timestamp=\"{3}\", oauth_token=\"{4}\", oauth_signature=\"{5}\", oauth_verifier=\"{6}\", oauth_version=\"1.0\"",
                    consumerKey, nonce, oAuthSignatureMethod, totalSeconds, token.Token, Globals.ProperUrlEscape(oAuthSignature),
                    pin);

            var request = (HttpWebRequest) WebRequest.Create(new Uri(baseAddress));

            request.Method = method;
            request.Headers.Add("Authorization", oAuthHeader);

            try
            {
                var response = request.GetResponse();

// ReSharper disable AssignNullToNotNullAttribute
                using (var sr = new StreamReader(response.GetResponseStream()))
                {
                    var result = sr.ReadToEnd();
                    var collection = HttpUtility.ParseQueryString(result);
                    return new OAuthToken(collection["oauth_token"], collection["oauth_token_secret"]);
                }
// ReSharper restore AssignNullToNotNullAttribute
            }
            catch (WebException ex)
            {
                throw new OAuthException(ex.Message);
            }
        }

        ///<summary>
        /// Allows a Consumer application to use an OAuth Request Token to request user authorization.
        ///</summary>
        ///<param name="myToken">Unauthorized request token to authorize</param>
        /// <remarks>This method fulfills <a href="http://oauth.net/core/1.0/#auth_step2">Section 6.2</a> of the <a href="http://oauth.net/core/1.0/#anchor9">OAuth 1.0 authentication flow</a>. Desktop applications must use this method (and cannot use oauth/authenticate).</remarks>
        /// <exception cref="ArgumentNullException"><paramref name="myToken" /> is <c>null</c>.</exception>
        public static void SendUserToAuthorization(OAuthToken myToken)
        {
            if (myToken == null) throw new ArgumentNullException("myToken");
            try
            {
                System.Diagnostics.Process.Start(string.Format("https://api.twitter.com/oauth/authorize?oauth_token={0}", myToken.Token));
            }
// ReSharper disable EmptyGeneralCatchClause
            catch (System.ComponentModel.Win32Exception)
            { }
// ReSharper restore EmptyGeneralCatchClause
        }
    }
}